﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace DotNetCommon;

/// <summary>
/// 基于 <c>AsyncLocal</c> 简单封装的代码执行上下文, 用于在异步代码中传递数据, 示例:
/// <code>
/// public async Task ProcessRequest()
/// {
///     // 从 request 中读取用户Id
///     var userId = 1;
///     // 将 userId 存储到异步上下文中, 方便后续的代码中随时读取
///     await RunningContext.RunInContextAsync(DoBusiness, dic => dic["id"] = id);
/// }
/// 
/// public async Task DoBusiness()
/// {
///     var userId = RunningContext.Current.GetValue&lt;int>("id");
///     //...
/// }
/// </code>
/// </summary>
/// <remarks>参照: <seealso href="https://blog.csdn.net/u010476739/article/details/121395350"/></remarks>
public class RunningContext
{
    private RunningContext(ConcurrentDictionary<object, object> dic)
    {
        AssertUtil.NotNull(dic);
        _id = Interlocked.Increment(ref _counter);
        this._dic = dic;
    }
    private static readonly AsyncLocal<RunningContext> Context = new();
    /// <summary>
    /// 当前代码执行的上下文环境
    /// </summary>
    public static RunningContext Current
    {
        get
        {
            if (Context.Value == null)
            {
                lock (typeof(RunningContext))
                {
                    Context.Value ??= new RunningContext(new ConcurrentDictionary<object, object>());
                }
            }
            return Context.Value;
        }
    }
    private ConcurrentDictionary<object, object> _dic { get; set; }

    private static int _counter;
    private readonly int _id;
    public int Id => _id;

    /// <summary>
    /// 开启一个新的异步上下文环境(创建一个新的 ConcurrentDictionary, 里面的数据从现在的上下文中拷贝初始化)
    /// </summary>
    public static async Task RunInContextAsync(Func<Task> act, Action<ConcurrentDictionary<object, object>> setDataAct = null)
    {
        var dic = Context.Value?._dic == null ? new ConcurrentDictionary<object, object>() : new ConcurrentDictionary<object, object>(Context.Value._dic);
        setDataAct?.Invoke(dic);
        Context.Value = new RunningContext(dic);
        await act();
    }

    /// <summary>
    /// 开启一个新的异步上下文环境(创建一个新的 ConcurrentDictionary, 里面的数据从现在的上下文中拷贝初始化)
    /// </summary>
    public static async Task<T> RunInContextAsync<T>(Func<Task<T>> act, Action<ConcurrentDictionary<object, object>> setDataAct = null)
    {
        var dic = Context.Value?._dic == null ? new ConcurrentDictionary<object, object>() : new ConcurrentDictionary<object, object>(Context.Value._dic);
        setDataAct?.Invoke(dic);
        Context.Value = new RunningContext(dic);
        return await act();
    }

    /// <summary>
    /// 开启一个新的异步上下文环境(创建一个新的 ConcurrentDictionary, 里面的数据从现在的上下文中拷贝初始化)
    /// </summary>
    public static void RunInContext(Action act, Action<ConcurrentDictionary<object, object>> setDataAct = null)
    {
        RunInContextAsync(() =>
        {
            act();
            return Task.CompletedTask;
        }, setDataAct).Wait();
    }

    /// <summary>
    /// 开启一个新的异步上下文环境(创建一个新的 ConcurrentDictionary, 里面的数据从现在的上下文中拷贝初始化)
    /// </summary>
    public static T RunInContext<T>(Func<T> func, Action<ConcurrentDictionary<object, object>> setDataAct = null)
    {
        return RunInContextAsync(() =>
        {
            return Task.FromResult(func());
        }, setDataAct).Result;
    }

    #region 对当前 ctx 中的 字典进行的通用操作
    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.TryGetValue(...)
    /// </summary>
    public bool TryGetValue(object key, out object val)
    {
        return _dic.TryGetValue(key, out val);
    }
    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.TryGetValue(...)
    /// </summary>
    public bool TryGetValue<T>(object key, out T val)
    {
        var flag = _dic.TryGetValue(key, out var tmp);
        if (flag)
        {
            val = (T)tmp;
        }
        else
        {
            val = default;
        }
        return flag;
    }

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.TrySetValue(...)
    /// </summary>
    public bool TrySetValue(object key, object val)
    {
        return _dic.TryAdd(key, val);
    }

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>[key] = val
    /// </summary>
    public void SetValue(object key, object val)
    {
        _dic[key] = val;
    }

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.ContainsKey(...)
    /// </summary>
    public bool ContainsKey(object key)
    {
        return _dic.ContainsKey(key);
    }

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>[key]
    /// </summary>
    public object GetValue(object key)
    {
        return _dic[key];
    }

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>[key]
    /// </summary>
    public T GetValue<T>(object key)
    {
        return (T)_dic[key];
    }

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.Remove(...)
    /// </summary>
    public bool Remove(object key) => _dic.TryRemove(key, out _);

    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.Count
    /// </summary>
    public int Count => _dic.Count;
    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.Clear()
    /// </summary>
    public void Clear() => _dic.Clear();
    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.Keys.ToList()
    /// </summary>
    public List<object> Keys => [.. _dic.Keys];
    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.Values.ToList()
    /// </summary>
    public List<object> Values => [.. _dic.Values];
    /// <summary>
    /// 就是对环境中的容器 IDictionary&lt;object, object>.ToList()
    /// </summary>
    public List<KeyValuePair<object, object>> All => [.. _dic];
    #endregion
}
